using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using CommonTypes;
using MVCFramework.Models;
using BuildingBasicDomain;
using CommonTypes.Events;
using MVCFramework.Presenter.Commands;
using MVCFramework.Presenter.States;
using MVCFramework.Presenter.Selections;
using MVCFramework.View;
using MVCFramework;

using System.Reflection;

namespace MVCFramework.Presenter
{
    [CLSCompliant(true)]
    [DebuggerDisplay ("id = {Id} related to model {_model.Id}")]
    public class Presenter : IPresenter , IViewActionListener 
    {
        #region Id Members
        private MVCFramework.Id<MVCFramework.IPresenter> _id;
        public MVCFramework.Id<MVCFramework.IPresenter> Id
        {
            get
            {
                if (_id.IsEmpty) return "Presenter";
                return _id;
            }
            set
            {
                _id = value;
            }
        }

        #endregion

        const BindingFlags DEFAULT_BINDING_FLAGS = 
            BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;

        private static readonly LoggerAdapter log = new LoggerAdapter(typeof(IPresenter));
        public IElementCreator _creator; // this has no reason to BE HERE REMOVE THIS [OBJECT CREATION] to model 

        # region " Events decleration "

        public event EventHandler OnSelection;
        public event EventHandler OnStateChange;

        #endregion

        # region "MenuHandling "

        public void DispatchMenuEvent(MVCFramework .Menus .IMenuCommand command)
        {
            ControllState.DispatchMenuEvent(command);
        }

        public void DispatchMenuEvent(string command)
        {
            ControllState.DispatchMenuEvent(command);
        }

        public ArrayList ParseContextMenu()
        {
            return ControllState.ParseContextMenu();
        }

        #endregion

        # region  " private data declaration "

        protected SelectionManager _selectionHandler;
        private CommandHandler _commandHandler;
        protected List<IView> _views;
        protected IModel _model;
        public IModel Model
        {
            get
            {
                return _model;
            }
            set
            {
                _model = value;
            }
        }
        protected ArrayList _selectedObjects;

        private ControllState _modelstate;
        public ControllState ControllState
        {
            set
            {
                this._modelstate = value;
               ControllState . initModel(_model);
               ControllState.initPresenter(this);
                if (OnStateChange != null)
                {
                    OnStateChange(this, new EventArgs());
                    NotifyViews();
                }
            }
            get { return _modelstate; }
        }

        private IView activeView;
        public IView ActiveView
        {
            set
            {
                activeView = value;
            }

            get
            {
                return activeView;
            }
        }

        #endregion

        public Presenter(IModel model)
        {
            log.Info(" Presenter " + Id  + "Initialization Started ");
            this._model = model;

            InitilizeHook();
            
            this._selectionHandler = new SelectionManager(this,model);
            this._commandHandler = new CommandHandler();
            this._views = new List<IView>();

          //  ControllState.initModel(_model);
           // ControllState.initPresenter(this);

            InitilizeMenuesStatesHook();
            InitilizeState();

            log.Info(" Presenter " + Id + "Initialization Finished ");

        }
        public Presenter(IModel model, string id) : this(model) { Id = id; }

        protected virtual void InitilizeHook()
        {
  
        }
        protected virtual void InitilizeMenuesStatesHook()
        {
        }
        protected virtual void InitilizeState()
        {
            log.Info(" Presenter" + Id + " Initialiazing States ");
            _modelstate = new NullState();
        }


        public void AddLine(ILine elasticLine)
        {
            List<ILine>lines = _model.GetObject <List<ILine>>(ModelData .ElasticLines) ;

            if (lines != null)
            {
                if (!lines.Contains(elasticLine))
                    lines.Add(elasticLine);
            }
            else
            {
                lines = new List<ILine>();
                lines.Add(elasticLine);
                 _model.SetObject(ModelData.ElasticLines, lines);
            }

        }
        public void RemoveLine(ILine elasticLine)
        {
            IList<ILine> lines =((IModelExplicit)_model ).GetObject <IList <ILine >>(ModelData .ElasticLines );

            if (lines != null)
                if (lines.Contains(elasticLine))
                    lines.Remove (elasticLine);
        }

        # region "IViewActionListener Implementation "

		public virtual bool MouseDownHandler(Object sender, ActionEventArgs e)
		{
            log.Info("Presenter's " + this.Id + " MouseDown Handler Invoked  By Senedr " + sender .ToString ());
            if (!_modelstate.IsNull)
                _modelstate.MouseDownHandler (sender, e);
            return false;     
		}

        public virtual bool MouseMoveHandler(Object sender, ActionEventArgs e)
        {
         //   log.Info("MouseMove Handler Invoked  By Senedr " + sender.ToString());
            SelectSnapPoints(e);
         //   Console.WriteLine(e.Coordinates[0] + " " + e.Coordinates [1] + " " + e.Coordinates [2]);
            if(!_modelstate .IsNull )
            _modelstate.MouseMoveHandler(sender, e);
			return false;
		}

        private void SelectSnapPoints(ActionEventArgs e)
        {
            if (e.List  != null)
                foreach (object _object in e.List)
                    if (_object is IPoint  )
                    {
                        DrawSelectedPoint(_object); 
                        NotifyViews<IViewForm>();
                        return;
                    }
        }

        public virtual bool MouseUpHandler(Object sender, ActionEventArgs e)
        {
            log.Info("Presenter's " + this.Id + " MouseUp Handler Invoked  By Senedr " + sender.ToString());
            if (!_modelstate.IsNull)
            _modelstate.MouseUpHandler (sender, e);
			return false;
		}
		
        # endregion  

        #region "command handling block "

        public void AddCommand(Command commmand)
        {
            log.Info(" Command " + commmand.Description + "Added To Presenter's :" + Id + " Command Stack ");

            _commandHandler.AddCommand(commmand);
           // NotifyViews();
        }

        public void Undo(int numOfCommands)
        {
            log.Info(" Presenter " + Id + " Undo " + numOfCommands);
            _commandHandler.UndoCommands(numOfCommands);
            NotifyViews();
            NotifyPresenters();
        }
        public void Undo()
        {
            _commandHandler.UndoCommands();
            NotifyViews();
        }
        public void Redo(int numOfCommands)
        {
            log.Info(" Presenter " + Id + " Redo " + numOfCommands);
            _commandHandler.RedoCommands(numOfCommands);
            NotifyViews();
            NotifyPresenters();
        }
        public void Redo()
        {
            _commandHandler.RedoCommands();
            NotifyViews();

        }

        # region " Add Remove - Requests to Models "

        public bool RequestAddition<T>(T _object) where T :class,IModelData 
        {
           // Debug.Assert(_object != null, "Added object is Null");
            if (_object == null) return false;

            Command command = new AddObjectCommand<T>(_model, _object);
            if (IsExecutedCommand(command))
            {
                return ProccesAddition<T>(_object);// NotifyViews();
            }
            else
            {
                log.Warn (" Object " + _object.ToString() + " Failed to Be added to model " + _model.ToString());
                return false;
            }
        }
        public bool RequestAddition<T>(IList <T> _objects) where T : class, IModelData
        {
            //Debug.Assert(_objects != null, "Added object is Null");
            Debug.Assert(_objects.Count != 0, "Added objects are Null");
            if (_objects == null) return false;

                Command command = new AddObjectCommand<T>(_model,  _objects );
                if (IsExecutedCommand(command))
                {
                    ProccesAddition<T>(_objects); NotifyViews();
                    return true;
                }
                else
                {    
                    log.Warn (" Object " + _objects.ToString() + " Failed to Be added to model "  + _model .ToString ());
                    return false;
                 //   throw new ModelExeption("Cannot add " + _objects.ToString() + "To Models");
                }
        }

        protected virtual void  ProccesAddition<T>(IList <T> _objects) where T : class, IModelData
        {
           foreach (T _object in _objects)
               ProccesAddition<T>(_object);
        }
        protected virtual bool ProccesAddition<T>(T _object) where T : class, IModelData
        {
            //CheckDeepPersistanse(_object);
           // if (CheckPersistance(_object.GetType(), false ))
              //  AttachEventHandler(_object);
            return true;
        }

        public bool RequestRemoval<T>(T _object) where T : class, IModelData
        {
            Command command = new RemoveObjcetCommand<T>(_model, _object);
            if (IsExecutedCommand(command))
            {
                ProccesRemoval<T>(_object);
                NotifyViews();
                return true;
            }
            else throw new ModelExeption("Cannot Remove " + _object.ToString() + " From Models");
        }
        public bool RequestRemoval<T>(IList<T> _objects) where T : class, IModelData
        {
            Command command = new RemoveObjcetCommand<T>(_model, _objects);
            if (IsExecutedCommand(command))
            {
                ProccesRemoval<T>(_objects);
                NotifyViews();
                return true;
            }   
            else throw new ModelExeption("Cannot Remove " + _objects.ToString() + " From Models");
        }

        protected virtual void ProccesRemoval<T>(T _object) where T : class, IModelData
        {
            if (_object is ISelectable)
                if (SelectedObjects.Contains(_object as ISelectable))
                    SelectedObjects.Remove(_object as ISelectable);
        }
        protected virtual void ProccesRemoval<T>(IList<T> _objects) where T : class, IModelData
        {

        }

        private bool ExecuteCommandOnSelection(Command command, Selection selection)
        {
            throw new NotImplementedException("Not implemented ");
        }          
        private bool IsExecutedCommand(Command command )
        {
            if (command.Execute())
            {
                AddCommand(command);
                return true;
            }
            else
                return false;
        }

        # endregion 
    
        protected void RequestPropertyChange(object referanceObject, string propertyName, object newValue)
        {
            if (ProperPropertyValue(referanceObject, propertyName, newValue))
            {
                System.ComponentModel.PropertyDescriptorCollection collection = System.ComponentModel.TypeDescriptor.GetProperties(referanceObject);
                System.ComponentModel.PropertyDescriptor descriptor = collection.Find(propertyName, true);
                object oldValue = referanceObject.GetType().GetProperty(propertyName).GetGetMethod().Invoke(referanceObject, null);

                MVCFramework.Presenter.Commands.Command changeValueCommand = new MVCFramework.Presenter.Commands.ChangeValueCommand(descriptor, referanceObject, oldValue, newValue);

                changeValueCommand.Execute();
                AddCommand(changeValueCommand);
            }

        }

        protected virtual bool ProperPropertyValue(object referanceObject, string propertyName, object newValue)
        {
            return true;
        }
 
        #endregion

        #region  "Selection handling block"

        public virtual  void  AddSelection (ISelectable selected)
        {
            if (OnSelection != null)
                OnSelection(selected, new EventArgs());

            _selectionHandler.Add (selected);
        }

        public virtual void RemoveSelection(ISelectable selected)
        {
            _selectionHandler.Remove(selected);
        }

        public virtual void ClearSelection()
        {
            _selectionHandler.Clear();
        }

        public virtual void DeleteSelection()
        {
            _selectionHandler.deleteSelection();
        }

        public IList  <ISelectable> SelectedObjects
        {
            get
            {
                return _selectionHandler.CurrentSelection;
            }
        }

        protected virtual void ApplyCommandOnSelection(IEnumerable  collection,ChangeValueCommand command)
        {
            foreach (object  type in collection)
            {
               Command _command =  command.Applicator(type);
               _command.Execute();
               AddCommand(_command);
            }
        }

        #endregion 

        #region "Directives  to Views "

        public void SetSelectedView(object sender, EventArgs e)
        {
			if (sender is IView)
                ActiveView = sender as IView;
        }

        public void drawLine(ILine line)
         {
             foreach (IViewForm view in _views)
                 view.drawLine(line);
         }

         #region  "DELETE THIS WHEN FINISH "
        public virtual void PresenterTestRoutine()
        {

        }

   
        public void DrawSelectedPoint(object point)
        {
            _views.ForEach(delegate(IView _view)
            {
                if (_view is IViewForm) { }
               // ((IViewForm)_view).CurrentlySelected (point);
            });
        }

        #endregion

        public virtual void AddView(IView _view)
        {

            if (!_views.Contains(_view))
            {
                log.Info("View " + _view .Id + " Added To presenter  " + Id);
                _views.Add(_view);
                if (_view is IViewForm)
                {
                    ((IViewForm)_view).OnViewActivated += new EventHandler<EventArgs>(Presenter_OnViewActivated);
                    ((IViewForm)_view).Closed += new EventHandler(Presenter_ViewClosed);
                    ((IViewForm)_view).SetTitle("View " + _views.Count);
                    if( MainView != null)
                    ((MVCFramework.View.IViewForm)_view).MdiParent = this.MainView .GetForm();
                    ((MVCFramework.View.IViewForm)_view).Show();

                }              
                
            }
        }

        public virtual void RemoveView(IView _view)
        {
            if (_views.Contains(_view))
                _views.Remove(_view);
        }

        public void Presenter_ViewClosed ( object sender , EventArgs Args )
        {
            if (sender is IViewForm)
            {
                log.Info("View " + ((IViewForm) sender) .Id + " Removed From Presenter  " + Id);
                RemoveView(sender as IView);
            }
        }

        protected virtual void Presenter_OnViewActivated(object sender , EventArgs args)
        {
            if (sender is IViewForm)
            {
                ActiveView = sender as IView;
                if (MainView != null)
                {
                    MainView.ActivatedForm = ((IViewForm)ActiveView);
                  //  MainView.PropertyGridDisplay(((IViewForm)ActiveView).ViewStateAbstract);
                }
            }
            
            DoProcessActiveView();
        }

        protected virtual void DoProcessActiveView()
        {
          
        }

        /// <summary>
        /// Notify all Views 
        /// </summary>
        public virtual void NotifyViews()
        {
            log.Info("Views Notifided from Presenter " + Id );
            Console.WriteLine("presenter notifies views");
            foreach (IView _view in _views )
                _view.Update();
        }
        /// <summary>
        /// notifies only Views of Type T1
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        public void NotifyViews<T1>() //May Add small logic Interpreter
        {
            log.Info("Views Notifided from Presenter " + Id);
            Console.WriteLine("presenter notifies views");
            foreach (IView _view in _views)
                if (_view is T1)
                    _view.Update();
        }
        # endregion 

        # region "this Block is to be Deleted persistance checking " 
        //this block finds the property that had changed value and stores the ,old , new values .
        //propery level undo redo.


        [Obsolete("Do not use that ", false)]
        void Presenter_OnDataValueChanged(object sender, PersistEventArgs e)
        {

            if (sender != null && e.propertyDescriptor != null && e.FinalValue != e.InitValue)
            {
                Command propertychanged = new ChangeValueCommand(e.propertyDescriptor,
                    sender, e.InitValue, e.FinalValue);
                AddCommand(propertychanged);
            }


        }

        EventHandler<PersistEventArgs> OnModifyHandler;

        private bool CheckPersistance(Type type, bool inheritance)
        {
            if (!type.IsClass) return false;
            try
            {
                PropertyInfo[] properties = type.GetProperties();
                foreach (PropertyInfo property in properties)
                    foreach (Attribute artribute in property.GetCustomAttributes(inheritance))
                        if (artribute is PersistAtrribute)
                            return true;

            }
            catch { } 
            return false;
        }

        private void  PersistRoutine (object sender , PersistEventArgs Args)
        {
            PropertyDescriptor descriptor = Args.propertyDescriptor;

            MemberInfo mi;
            Type type = sender.GetType ();
            mi = GetMemberToInvoke(type, Args);
            if (mi == null) return;
            InvokeMemberInfo(sender, Args, mi);
        }

        private MemberInfo GetMemberToInvoke(Type type, PersistEventArgs Args)
        {
            MemberInfo mi;
            MemberInfo[] info = type.GetMember(Args.Descriptor, DEFAULT_BINDING_FLAGS);

            if (info != null && info.Length >0)
            {
                mi = info[0];
                return mi;
            }
            else
            {
                if (type.BaseType != null)
                    return GetMemberToInvoke(type.BaseType, Args);
                return null;
            }
            
        }

        private void InvokeMemberInfo(object sender, PersistEventArgs Args, MemberInfo mi)
        {
            if (mi is PropertyInfo)
            {
                PropertyInfo prop = (PropertyInfo)mi;
                MethodInfo method = prop.GetSetMethod();
                object[] aa = new object[] { Args.FinalValue };

                method.Invoke(sender, aa);
            }
            else
            {
                if (mi is FieldInfo)
                {
                    FieldInfo field = (FieldInfo)mi;
                    field.SetValue(sender, Args.FinalValue);
                }
                else
                {
                    MethodInfo method = (MethodInfo)mi;
                    object[] aa = new object[] { Args.FinalValue };
                    method.Invoke(sender, aa);
                }
            }
        }

        private void CheckDeepPersistanse(object type)
        {
                if (!type.GetType().IsClass) return;

                FieldInfo[] fields = type.GetType().GetFields(DEFAULT_BINDING_FLAGS);
                foreach (FieldInfo field in fields)
                {
                    object _object = field.GetValue(type);
                    if (_object != null)
                    {
                        CheckDeepPersistanse(_object);

                        if (CheckPersistance(_object.GetType(), true)) 
                            HandlePersistance(field, _object);
                    }
                }
        }

        private void HandlePersistance(FieldInfo field, object _object)
        {
            Console.WriteLine(field.Name);
            AttachEventHandler(_object);
        }

        private void AttachEventHandler(object _object)
        {
            Type type = _object.GetType();
            EventInfo[] events = type.GetEvents();
            foreach (EventInfo _event in events)
                if (_event.Name == "OnModify")
                    _event.AddEventHandler(_object, OnModifyHandler);

        }

        private void DetachEventHandlers(object _object)
        {
            Type type = _object.GetType();
            EventInfo[] events = type.GetEvents();
            foreach (EventInfo _event in events)
                if (_event.Name == "OnModify")
                    _event.RemoveEventHandler(_object, OnModifyHandler);
        }

        #endregion

        #region IPresenter Members

        public IList<IView> GetViews()
        {
            return _views;
        }

        #endregion

        private void NotifyPresenters()
        {
            if (OnPresenterModifidedModel != null)
                OnPresenterModifidedModel(this, new EventArgs());
        }

        #region IPresenter Members

        public event EventHandler OnPresenterModifidedModel;

        public void PresenterModifidedModelHandler(object sender, EventArgs Args)
        {
           // UpadateModels();
            NotifyViews();
        }

        private IMainView _mainView;
        public IMainView MainView
        {
            get
            {
                return _mainView;
            }
            set
            {
                _mainView = value;
            }
        }

        public virtual void SetControllState(Type TypeofState)
        {
            if (!CanSetToState(TypeofState)) return;

            ControllState State = Activator.CreateInstance(TypeofState) as ControllState ;
            State.initModel(_model);
            State.initPresenter(this);
            ControllState = State;
        }

        public virtual void SetControllState(Type TypeofState,Type Selectionstrategy)
        {
            SetControllState(TypeofState);
            ControllState.InitializeSelectionStrategy(Selectionstrategy);
        }

        protected bool CanSetToState(Type TypeofState)
        {
            return true;
        }

        public IList<MVCFramework.Menus.IMenuCommand> GetCommands()
        {
            throw new Exception("The method or operation is not implemented.");
        }

        #endregion

        #region IPresenter Members

        public IView  GetView(string id)
        {
            foreach (IView view in _views)
                if (view.Id == id)
                    return view;
            return null;
        }

        /// <summary>
        /// Notifies Vies That Satisfy the predicate. most general 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="predicate"></param>
        public void NotifyViews<T>(Predicate<T> predicate)  // in order to avoid interpreting logic 
        {
            foreach (IView view in GetViews())
              if(view is T) if (predicate((T)view)) (view).Update();
        }


        #endregion
    }
}
